
package UMC.Web;

import UMC.Data.Sql.Initializer;
import UMC.Data.Utility;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.*;

class WebRuntime {
    public static class AbortException extends RuntimeException {

    }

    WebRequest request;


    WebResponse response;


    WebFlow _flow;
    WebActivity _activity;
    WebContext context;
    WebClient client;

    private WebRuntime(String model, String cmd, WebMeta header, WebClient client) {
        this.request = new WebRequest(model, cmd, header, client);

        this.response = new WebResponse(client);

        this.client = client;
    }

    private static class MappingFLow extends WebFlow {


        @Override
        public WebActivity firstActivity() {

            WebRequest webRequest = this.context().request();//.Request;
            Map<String, Class<WebActivity>> dic = activities.get(webRequest.model());//[webRequest];
            if (dic.containsKey(webRequest.cmd())) {
                try {
                    Constructor<WebActivity> constructor = dic.get(webRequest.cmd()).getDeclaredConstructor();//[0];
                    constructor.setAccessible(true);
                    return (WebActivity) constructor.newInstance();
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
            return WebActivity.Empty;


        }
    }

    private static class MappingActivityFactory implements IWebFactory {
        @Override
        public void init(WebContext context) {

        }

        @Override
        public WebFlow flowHandler(String mode) {
            if (activities.containsKey(mode)) {

                return new MappingFLow();

            }
            return WebFlow.Empty;
        }


    }

    private static class MappingFlowFactory implements IWebFactory {
        @Override
        public void init(WebContext context) {

        }

        @Override
        public WebFlow flowHandler(String mode) {
            if (flows.containsKey(mode)) {
                try {

                    Constructor<WebFlow> constructor = flows.get(mode).get(index).getDeclaredConstructor();//[0];
                    constructor.setAccessible(true);
                    return (WebFlow) constructor.newInstance();//[mode][index]
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }

            }
            return WebFlow.Empty;
        }

        int index = 0;

        public MappingFlowFactory(int i) {
            this.index = i;
        }


    }

    private static IWebFactory[] getFactory(String model) {
        if (flows.containsKey(model)) {
            int len = flows.get(model).size();
            List<IWebFactory> list = new LinkedList<>();
            for (int i = 0; i < len; i++) {
                list.add(new MappingFlowFactory(i));

            }
            return list.toArray(new IWebFactory[0]);
        }
        return new IWebFactory[0];

    }

    public static WebContext processRequest(String model, String cmd, WebMeta header, WebClient client) {

        WebRuntime runtime = new WebRuntime(model, cmd, header, client);

        try {
            runtime.DoFactory();
        } catch (AbortException ignored) {
        }


        return runtime.context;


    }


    static Map<String, WebAuthType> authKeys = new HashMap<>();
    static List<Class<IWebFactory>> facClas = new LinkedList<>();
    static Map<String, Map<String, Class<WebActivity>>> activities = new HashMap<>();
    static Map<String, List<Class<WebFlow>>> flows = new HashMap<>();

    public static void register(Class cl) {
        if (cl.isAnnotationPresent(Apiumc.class)) {
            Annotation[] annotations = cl.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof Apiumc) {
                    Apiumc mapping = (Apiumc) annotation;//cl.getAnnotation(Mapping.class);
                    if (!Utility.isEmpty(mapping.model()) && !Utility.isEmpty(mapping.cmd())) {

                        if (WebActivity.class.isAssignableFrom(cl)) {
                            if (!activities.containsKey(mapping.model())) {
                                activities.put(mapping.model(), new HashMap<>());
                            }
                            activities.get(mapping.model()).put(mapping.cmd(), cl);
                            authKeys.put(String.format("%s.%s", mapping.model(), mapping.cmd()), mapping.auth());


                        }
                    } else if (!Utility.isEmpty(mapping.model())) {

                        if (WebFlow.class.isAssignableFrom(cl)) {
                            if (!flows.containsKey(mapping.model())) {
                                flows.put(mapping.model(), new LinkedList<>());
                            }
                            List<Class<WebFlow>> list = flows.get(mapping.model());
                            if (!list.contains(cl)) {
                                list.add(cl);

                                authKeys.put(mapping.model(), mapping.auth());
                            }
                        }
                    } else if (Initializer.class.isAssignableFrom(cl)) {
                        try {
                            Initializer.put((Initializer) cl.newInstance());
//                            if (!Initializer.Initializers().contains(initializer)) {
//                                Initializer.Initializers().add(initializer);
//                            }
                        } catch (InstantiationException | IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    } else if (IWebFactory.class.isAssignableFrom(cl)) {

                        if (!facClas.contains(cl)) {
                            facClas.add(cl);
                        }
                    }
                }
            }
        }


    }

    static void register(List<String> clses) {


        for (String cls : clses) {
            try {
                register(Class.forName(cls));
            } catch (ClassNotFoundException e) {
                // e.printStackTrace();
                continue;
            }
        }
    }


    void DoFactory() {


        context = new WebContext(this);

        List<IWebFactory> factorys = new LinkedList<>();

        for (Class<IWebFactory> cls : facClas) {
            try {

                Constructor<IWebFactory> constructor = cls.getDeclaredConstructor();//[0];
                constructor.setAccessible(true);
                IWebFactory webFactory = (IWebFactory) constructor.newInstance();
                webFactory.init(context);
                factorys.add(webFactory);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }

        factorys.addAll(Arrays.asList(getFactory(request.model())));
        factorys.add(new MappingActivityFactory());
        for (int Index = 0; Index < factorys.size(); Index++) {

            IWebFactory flowFactory = factorys.get(Index);

            WebFlow flow = flowFactory.flowHandler(this.request.model());
            if (!Objects.equals(flow, WebFlow.Empty)) {
                flow._context = context;
                WebActivity active = flow.firstActivity();
                if (!Objects.equals(active, WebActivity.Empty)) {
                    this._flow = flow;
                    this._activity = active;
                    active._context = context;
                    active.processActivity(this.request, this.response);
                    break;
                }
            }
        }


    }

}
